﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using M3G;

namespace MyTestProject {
    public class TestLOD {

        /**
         * LODが無効の時に処理を行わないことを確認するテスト
         * */
        [Fact]
        public void TestSelectLOD1 () {
            var wld = new World ();
            var grp = new Group ();
            var cam = new Camera ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp.AddChild (nod1);
            grp.AddChild (nod2);
            wld.AddChild (grp);
            wld.AddChild (cam);

            grp.SetLODEnable (false, 0);
            grp.SelectLOD (cam, 100, 100);  // do nothing

            Assert.Equal (-1, grp.LODChild);
            Assert.Equal (true, grp.Child[0].RenderingEnabled);
            Assert.Equal (true, grp.Child[1].RenderingEnabled);
        }


        /**
         * 最も簡単なLODのテスト
         * ra=0, slod=50, rb=100, 従って
         * index=0, blendfactor=0.5
         * */
        [Fact]
        public void TestSelectLOD2 () {
            var wld = new World ();
            var grp = new Group ();
            var cam = new Camera ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp.AddChild (nod1);
            grp.AddChild (nod2);
            wld.AddChild (grp);
            wld.AddChild (cam);

            cam.SetParallel (2.0f, 1.0f, 0.1f, 100.0f); // 2 unit length
            nod1.LODResolution = 0;     //   0 features/unit
            nod2.LODResolution = 100;   // 100 features/unit

            grp.SetLODEnable (true, 0);
            grp.SelectLOD (cam, 100, 100);  // 100 features / 2 unit

            // node1 was enabled and the others are not
            Assert.Equal (0, grp.LODChild); // index0 = nod1
            Assert.Equal (0.5f, grp.LODBlendFactor);
            Assert.Equal (true, grp.Child[0].RenderingEnabled);
            Assert.Equal (false, grp.Child[1].RenderingEnabled);
        }

        /**
         * 最も詳細なモデルを選択するLODのテスト
         * ra=0, rb=100, slod=100, 従って
         * index=1, blendfactor=0
         * */
        [Fact]
        public void TestSelectLOD3 () {
            var wld = new World ();
            var grp = new Group ();
            var cam = new Camera ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp.AddChild (nod1);
            grp.AddChild (nod2);
            wld.AddChild (grp);
            wld.AddChild (cam);

            cam.SetParallel (2.0f, 1.0f, 0.1f, 100.0f); // 2 unit length
            nod1.LODResolution = 0;     //   0 features/unit
            nod2.LODResolution = 100;   // 100 features/unit

            grp.SetLODEnable (true, 0);
            grp.SelectLOD (cam, 200, 200);  // 200 features / 2 unit

            // node1 was enabled and the others are not
            Assert.Equal (1, grp.LODChild); // index0 = nod1
            Assert.Equal (0.0f, grp.LODBlendFactor);
            Assert.Equal (false, grp.Child[0].RenderingEnabled);
            Assert.Equal (true, grp.Child[1].RenderingEnabled);
        }

        /**
         * LOD選択の結果すべて無効になった事を確認するLODのテスト
         * slod=50, ra=100, rb=200, 従って
         * index=-1, blendfactor=0
         * */
        [Fact]
        public void TestSelectLOD4 () {
            var wld = new World ();
            var grp = new Group ();
            var cam = new Camera ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp.AddChild (nod1);
            grp.AddChild (nod2);
            wld.AddChild (grp);
            wld.AddChild (cam);

            cam.SetParallel (2.0f, 1.0f, 0.1f, 100.0f); // 2 unit length
            nod1.LODResolution = 100;   // 100 features/unit
            nod2.LODResolution = 200;   // 200 features/unit

            grp.SetLODEnable (true, 0);
            grp.SelectLOD (cam, 100, 100);  // 100 features / 2 unit

            // all nodes are disabled
            Assert.Equal (-1, grp.LODChild);
            Assert.Equal (false, grp.Child[0].RenderingEnabled);
            Assert.Equal (false, grp.Child[1].RenderingEnabled);
        }

        /**
         * LODResolutionが内部でソートされている事を確認するテスト
         * (内部実装的に子ノードは昇順でないと正常に動作しない)
         * */
        [Fact]
        public void TestSelectLOD5 () {
            var grp = new Group ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp.AddChild (nod1);
            grp.AddChild (nod2);
  
            nod1.LODResolution = 100;
            nod2.LODResolution = 200;

            Assert.Equal (nod2, grp.Child[0]);
            Assert.Equal (nod1, grp.Child[1]);

            nod1.LODResolution = 200;
            nod2.LODResolution = 100;

            Assert.Equal (nod2, grp.Child[0]);
            Assert.Equal (nod1, grp.Child[1]);
        }

        /**
         * 透視投影によりZに比例してLODが変化することを確認するテスト
         * ra=100, rb=200, slodは可変
         * */
        [Fact]
        public void TestSelectLOD6 () {
            var wld = new World ();
            var grp = new Group ();
            var cam = new Camera ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp.AddChild (nod1);
            grp.AddChild (nod2);
            wld.AddChild (grp);
            wld.AddChild (cam);

            cam.SetPerspective (45.0f, 1.0f, 0.1f, 100.0f); // 2 unit length
            nod1.LODResolution = 100;   // 100 features/unit
            nod2.LODResolution = 200;   // 200 features/unit


            grp.SetLODEnable (true, 0);

            // slod = 89.6
            cam.SetTranslation (0, 0, 10);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (-1, grp.LODChild);

            // slod = 179.3
            cam.SetTranslation (0, 0, 5);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (0, grp.LODChild);

            // slod = 298.8
            cam.SetTranslation (0, 0, 3);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (1, grp.LODChild);

            // slod = inf.
            cam.SetTranslation (0, 0, 0);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (1, grp.LODChild);
        }

        /**
         * LODオフセットによってLODが変化することを確認するテスト
         * ra=100, rb=200, slodは可変
         * */
        [Fact]
        public void TestSelectLOD7 () {
            var wld = new World ();
            var grp = new Group ();
            var cam = new Camera ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp.AddChild (nod1);
            grp.AddChild (nod2);
            wld.AddChild (grp);
            wld.AddChild (cam);

            cam.SetPerspective (45.0f, 1.0f, 0.1f, 100.0f); // 2 unit length
            nod1.LODResolution = 100;   // 100 features/unit
            nod2.LODResolution = 200;   // 200 features/unit


            grp.SetLODEnable (true, 0);

            // slod = 89.6
            cam.SetTranslation (0, 0, 10);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (-1, grp.LODChild);

            // slod = 179.3
            grp.LODOffset = 5.0f;
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (0, grp.LODChild);

            // slod = 298.8
            grp.LODOffset = 7.0f;
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (1, grp.LODChild);

            // slod = inf.
            grp.LODOffset = 10.0f;
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (1, grp.LODChild);
        }

        /**
         * ヒステリシスループを反映してLODが変化することを確認するテスト
         * ra=100, rb=200, slodは可変
         * */
        [Fact]
        public void TestSelectLOD8 () {
            var wld = new World ();
            var grp = new Group ();
            var cam = new Camera ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp.AddChild (nod1);
            grp.AddChild (nod2);
            wld.AddChild (grp);
            wld.AddChild (cam);

            cam.SetPerspective (45.0f, 1.0f, 0.1f, 100.0f); // 2 unit length
            nod1.LODResolution = 100;   // 100 features/unit
            nod2.LODResolution = 200;   // 200 features/unit


            grp.SetLODEnable (true, 0.2f);  // hysterisys=20%

            // slod = 89.6, 初期値は-1
            cam.SetTranslation (0, 0, 10);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (-1, grp.LODChild);

            // slod = 112.0, ヒステリシスループ内なので-1のまま (*1)
            cam.SetTranslation (0, 0, 8);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (-1, grp.LODChild);

            // slod = 179.3, 次のindex=0に移動
            cam.SetTranslation (0, 0, 5);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (0, grp.LODChild);

            // slod = 112.0, (*1)と同じ条件だが今度は0
            cam.SetTranslation (0, 0, 8);
            grp.SelectLOD (cam, 1000, 1000);
            Assert.Equal (0, grp.LODChild);


        }

        /**
         * SelectLOD()が子ノードに伝搬することを確認するテスト
         * ra=0, slod=50, rb=100
         * */
        [Fact]
        public void TestSelectLOD9 () {
            var wld = new World ();
            var grp1 = new Group ();
            var grp2 = new Group ();
            var cam  = new Camera ();
            var nod1 = new Light ();
            var nod2 = new Light ();
            grp1.AddChild (grp2);
            grp2.AddChild (nod1);
            grp2.AddChild (nod2);
            wld.AddChild (grp1);
            wld.AddChild (cam);

            cam.SetParallel (2.0f, 1.0f, 0.1f, 100.0f); // 2 unit length
            nod1.LODResolution = 0;   // 0 features/unit
            nod2.LODResolution = 100; // 100 features/unit

            grp2.SetLODEnable (true, 0);

            // slod = 50.0
            wld.SelectLOD (cam, 100, 100);

            Assert.Equal (0, grp2.LODChild);
            Assert.Equal (true, grp2.Child[0].RenderingEnabled);
            Assert.Equal (false, grp2.Child[1].RenderingEnabled);
        }

    }
}
